package com.rbac.application.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rbac.application.service.UserService;
import com.rbac.common.enums.BusinessErrorCode;
import com.rbac.common.exception.BusinessException;
import com.rbac.domain.dto.query.UserPageQuery;
import com.rbac.domain.dto.request.UserCreateRequest;
import com.rbac.domain.dto.request.UserUpdateRequest;
import com.rbac.domain.entity.User;
import com.rbac.domain.entity.UserRole;
import com.rbac.infrastructure.mapper.UserMapper;
import com.rbac.infrastructure.mapper.UserRoleMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final UserRoleMapper userRoleMapper;

    @Override
    public IPage<User> pageUsers(UserPageQuery query) {
        Page<User> page = new Page<>(query.getPage(), query.getSize());
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(StrUtil.isNotBlank(query.getUsername()), User::getUsername, query.getUsername())
                .like(StrUtil.isNotBlank(query.getNickname()), User::getNickname, query.getNickname())
                .like(StrUtil.isNotBlank(query.getEmail()), User::getEmail, query.getEmail())
                .like(StrUtil.isNotBlank(query.getPhone()), User::getPhone, query.getPhone())
                .eq(query.getStatus() != null, User::getStatus, query.getStatus())
                .orderByDesc(User::getCreatedTime);
        return this.page(page, wrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public User createUser(UserCreateRequest request) {
        if (this.checkUsernameExists(request.getUsername())) {
            throw new BusinessException(BusinessErrorCode.USERNAME_ALREADY_EXISTS);
        }

        User user = new User();
        user.setUsername(request.getUsername());
        user.setPassword(BCrypt.hashpw(request.getPassword()));
        user.setNickname(request.getNickname());
        user.setEmail(request.getEmail());
        user.setPhone(request.getPhone());
        user.setAvatar(request.getAvatar());
        user.setStatus(request.getStatus());
        user.setRemark(request.getRemark());
        user.setCreatedTime(LocalDateTime.now());
        
        this.save(user);

        if (CollUtil.isNotEmpty(request.getRoleIds())) {
            this.assignRoles(user.getId(), request.getRoleIds());
        }

        return user;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public User updateUser(UserUpdateRequest request) {
        User user = this.getById(request.getId());
        if (user == null) {
            throw new BusinessException(BusinessErrorCode.USER_NOT_FOUND);
        }

        user.setNickname(request.getNickname());
        user.setEmail(request.getEmail());
        user.setPhone(request.getPhone());
        user.setAvatar(request.getAvatar());
        user.setStatus(request.getStatus());
        user.setRemark(request.getRemark());
        user.setUpdatedTime(LocalDateTime.now());

        this.updateById(user);

        if (request.getRoleIds() != null) {
            this.assignRoles(user.getId(), request.getRoleIds());
        }

        return user;
    }

    @Override
    public void deleteUser(Long id) {
        if (id.equals(1L)) {
            throw new BusinessException(BusinessErrorCode.PERMISSION_DENIED);
        }
        this.removeById(id);
        userRoleMapper.deleteByUserId(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteBatchUsers(List<Long> ids) {
        if (ids.contains(1L)) {
            throw new BusinessException(BusinessErrorCode.PERMISSION_DENIED);
        }
        this.removeByIds(ids);
        userRoleMapper.deleteByUserIds(ids);
    }

    @Override
    public User getUserWithRoles(Long id) {
        return baseMapper.getUserWithRoles(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void assignRoles(Long userId, List<Long> roleIds) {
        userRoleMapper.deleteByUserId(userId);
        
        if (CollUtil.isNotEmpty(roleIds)) {
            List<UserRole> userRoles = roleIds.stream()
                    .map(roleId -> {
                        UserRole userRole = new UserRole();
                        userRole.setUserId(userId);
                        userRole.setRoleId(roleId);
                        userRole.setCreatedTime(LocalDateTime.now());
                        return userRole;
                    })
                    .toList();
            userRoleMapper.insertBatch(userRoles);
        }
    }

    @Override
    public List<String> getUserPermissions(Long userId) {
        return baseMapper.getUserPermissions(userId);
    }

    @Override
    public void resetPassword(Long id, String newPassword) {
        User user = new User();
        user.setId(id);
        user.setPassword(BCrypt.hashpw(newPassword));
        user.setUpdatedTime(LocalDateTime.now());
        this.updateById(user);
    }

    @Override
    public void updateLoginInfo(Long id, String loginIp) {
        User user = new User();
        user.setId(id);
        user.setLoginIp(loginIp);
        user.setLoginTime(LocalDateTime.now());
        this.updateById(user);
    }

    private boolean checkUsernameExists(String username) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername, username);
        return this.count(wrapper) > 0;
    }
}